package rpcserver

import (
	"context"
	"fmt"
	"net"

	// "bgs/grpc/client"
	"bp/internal/nsqserver/producer"

	grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
	"go.uber.org/zap"
	"google.golang.org/grpc"

	// "bp/cache"
	"bp/conf"
	"bp/db"
	internalGrpcMiddleware "bp/internal/middleware/rpc"
	backendapi "bp/internal/rpcserver/backendapi"
	"bp/internal/rpcserver/backendapi/coupon_imp"
	"bp/internal/rpcserver/backendapi/off_imp"
	mwebapi "bp/internal/rpcserver/mwebapi"
	webapi "bp/internal/rpcserver/webapi"
	"bp/internal/service/com"
	// "bp/internal/service/com/apportioner"
	"bp/internal/service/entity"
	// "bp/internal/util"
)

var (
	rebooted int = 0
	// grpcClientProxy *client.GrpcClientProxy
	rpcServerImpPtr *RpcServerImp
)

// ServerImp :grpc server proxy of bapi
type RpcServerImp struct {
	// dao  *db.PgDao
	slog *zap.SugaredLogger
	bs   *backendapi.BackendApiServer
	ws   *webapi.WebApiServer
	mws  *mwebapi.MWebApiServer
	// mwebapiRpc.MWebApiOpServiceServer
	// mwebapiRpc.MWebApiOpService
	// webapiRpc.CouponOpServiceServer
	// webapiRpc.CouponOpService
	// cacher           *cache.Adapter
}

// InitRpcServer 初始化rpc server
func InitRpcServer(
	slog *zap.SugaredLogger,
	pgConfPtr *conf.PgSection,
	vars *conf.VarsSection,
	grpcConfPtr *conf.GrpcSection,
	nsqConfPtr *conf.NsqSection,
	mwebConfPtr *conf.MWebSection,
	webConfPtr *conf.WebSection,
	// adp *cache.Adapter,
	// clientProxy *client.GrpcClientProxy,
	// cm *component.ComponentManger,
) {
	slog = slog.With("entry", "grpc")

	// grpc client proxy
	// grpcClientProxy = clientProxy

	// util
	// pgErrorChecker := util.NewPgErrorChecker(slog)

	// biz services
	rpcServerImpPtr = &RpcServerImp{
		slog: slog,
	}

	stopGrpcSignal := make(chan struct{})

	// grpc server 实现
	go RegisterGrpc(slog, pgConfPtr, grpcConfPtr, nsqConfPtr, mwebConfPtr, webConfPtr, vars, rpcServerImpPtr, stopGrpcSignal)

	// brandService := brandService.NewBrandService(pgdao, slog, wechatConfPtr, vars, clientProxy, adp)
	// enforcementService := enforcementService.NewEnforcementService(pgdao, slog, wechatConfPtr, vars, clientProxy, adp)
	// ledgerService := ledgerService.NewLedgerService(pgdao, slog, vars, clientProxy, adp)
	// orderService := orderService.NewOrderService(pgdao, slog, vars, clientProxy, adp)
	// paymentService := paymentService.NewPaymentService(pgdao, slog, wechatConfPtr, vars, clientProxy, adp)
	// productService := productService.NewProductService(pgdao, slog, vars, clientProxy, adp)
	// spokesmanService := spokesmanService.NewSpokesmanServiceImp(pgdao, slog, vars, clientProxy, adp)
	// userService := userService.NewUserService(pgdao, slog, wechatConfPtr, vars, clientProxy, adp)
	// warehouseService := warehouseService.NewWarehouseService(pgdao, slog, vars, clientProxy, adp)
	// wxpMinaService := wxpMinaService.NewMinaService(pgdao, slog, wechatConfPtr, vars, adp)

	var isStopGrpc bool

	for {
		<-stopGrpcSignal
		isStopGrpc = true
		if isStopGrpc {
			slog.Infof("3服务都停止,正式退出")
			return
		}
	}
}

func RegisterGrpc(
	slog *zap.SugaredLogger,
	pgConfPtr *conf.PgSection,
	grpcConfPtr *conf.GrpcSection,
	nsqConfPtr *conf.NsqSection,
	mwebConfPtr *conf.MWebSection,
	webConfPtr *conf.WebSection,
	vars *conf.VarsSection,
	rs *RpcServerImp,
	chStopSignal chan<- struct{},
) (err error) {
	defer func() {
		if r := recover(); r != nil {
			slog.Panicf("grpc recover panic:%s", r)
		}
		if rebooted < grpcConfPtr.MaxReboot {
			err = registerGrpc(slog, pgConfPtr, grpcConfPtr, nsqConfPtr, mwebConfPtr, webConfPtr, vars, rs)
		} else {
			chStopSignal <- struct{}{}
		}
	}()
	err = registerGrpc(slog, pgConfPtr, grpcConfPtr, nsqConfPtr, mwebConfPtr, webConfPtr, vars, rs)
	return
}

func registerGrpc(
	slog *zap.SugaredLogger,
	pgConfPtr *conf.PgSection,
	grpcConfPtr *conf.GrpcSection,
	nsqConfPtr *conf.NsqSection,
	mwebConfPtr *conf.MWebSection,
	webConfPtr *conf.WebSection,
	vars *conf.VarsSection,
	rs *RpcServerImp,
) (err error) {
	grpUrl := fmt.Sprintf("%s:%s", grpcConfPtr.Host, grpcConfPtr.Port)
	lis, err := net.Listen("tcp", grpUrl)
	if err != nil {
		slog.Fatalf("failed to listen: %v", err)
	}
	pgdao := db.New(pgConfPtr, slog)
	pub, err := producer.NewNsqProducerProxy(slog, pgdao, nsqConfPtr)
	if err != nil {
		slog.Errorf("pub start failed, %s", err.Error())
	}

	s := grpc.NewServer(
		grpc.UnaryInterceptor(
			grpc_middleware.ChainUnaryServer(
				internalGrpcMiddleware.UnaryRequestIDInterceptor(slog),
				internalGrpcMiddleware.UnaryBilinTokenInterceptor(slog, func(ctx context.Context, fullMethodName string, servingObject interface{}) bool {
					// todo:可以作为默认匹配 util.BeginWith(fullMethodName, "/bp.backendapi.CouponOpService/") && !util.Contains(fullMethodName, "/Ping")
					switch srv := servingObject.(type) {
					case *coupon_imp.CouponBackendApiImp:
						return srv.AllowBilinTokenMW(fullMethodName)
					default:
						return false
					}
				}),
				internalGrpcMiddleware.UnaryAuthorizationTokenInterceptor(slog, func(ctx context.Context, fullMethodName string, servingObject interface{}) bool {
					// todo:可以作为默认匹配 util.BeginWith(fullMethodName, "/bp.backendapi.OffOpService/") && !util.Contains(fullMethodName, "/Ping")
					switch srv := servingObject.(type) {
					case *off_imp.OffBackendApiImp:
						return srv.AllowAuthorizationTokenMW(fullMethodName)
					default:
						return false
					}
				}),
				internalGrpcMiddleware.UnaryPagerInterceptor(slog, func(ctx context.Context, fullMethodName string, servingObject interface{}) bool {
					// todo:可以作为默认匹配 util.Contains(fullMethodName, "/List") && !util.Contains(fullMethodName, "/Ping")
					switch srv := servingObject.(type) {
					case *coupon_imp.CouponBackendApiImp:
						return srv.AllowPagerMW(fullMethodName)
					default:
						return false
					}
				}),
				internalGrpcMiddleware.UnaryLoggingInterceptor(slog),
				internalGrpcMiddleware.UnaryRecoveryInterceptor(slog),
			),
		),
	)

	entityHub := entity.NewEntityHub(slog, pgdao, vars)
	comHub := com.NewComHub(slog, pgdao, vars, pub, entityHub)

	// 测试分摊代理
	// slog.Info("backendapi teset apportion begin")
	// chApportion := comHub.ApportionService.GetApportionWorker()
	// chApportion <- 11
	// slog.Info("backendapi teset apportion transactionID:%d", 2)
	// 启动 backend server
	rs.bs = backendapi.NewBackendApiServer(s, slog, pgdao, vars, entityHub, comHub)
	if rs.bs != nil {
		slog.Info("backendapi server(grpc) started")
	}

	// 启动 mwebapi server
	rs.mws = mwebapi.NewMWebApiServer(s, slog, pgdao, mwebConfPtr, vars, entityHub, comHub)
	if rs.mws != nil {
		slog.Info("mwebapi server(http) started")
	}

	// 启动 webapi server
	rs.ws = webapi.NewWebApiServer(s, slog, pgdao, webConfPtr, vars, pub, entityHub, comHub)
	if rs.ws != nil {
		slog.Info("webapi server(http) started")
	}
	slog.Info("server started")
	if err := s.Serve(lis); err != nil {
		slog.Fatalf("failed to serve: %v", err)
	}
	slog.Info("server stoped")
	return
}
